package analyseEvolutionPlateau;

import java.util.Iterator;

import plateau.Plateau;

import structureDeDonnee.Cell;
import structureDeDonnee.Matrice;
import structureDeDonnee.Position;

/**
 * class ayant pour but d'analyser un plateau pour essayer de savoir si
 * l'evolution asymptotique correspond à un vaisseau, à un oscillateur, à un
 * etat stable ou à un etat de mort
 */
public class AnalyseEvolutionTypeVaisseau implements AnalyseEvolution {

	/**
	 * essait pendant un certain nombre d'étape de calculer l'évolution
	 * asymptotique d'un plateau. renvoit le résultat des qu'il est trouvé, et
	 * resultatIndeterminé si le resultat n'est jamais trouvé.
	 * 
	 * @param p
	 * @param d
	 * @return l'evolution asymptotique trouvé ou un resultatIndeterminé
	 * @see ResultatAnalyseIndetermine
	 */
	@Override
	public ResultatAnalyse analyse(Plateau p, int d) {

		Plateau p1 = p.clone();
		Plateau p2 = p.clone();
		p2.etapeSuivante();
		int cpt1 = 1;
		int cpt2 = 2;

		Position translation = new Position();

		for (int i = 0; i < d; i++) {
			Matrice structure1 = p1.getMatrice();
			Matrice structure2 = p2.getMatrice();

			int queue;
			int periode;

			if (structure1.isEmpty()) {
				return new ResultatAnalyseMort(i);
			} else if (listesCellulesEgales(structure1, structure2)) {
				// on sait que l'evolution est de type oscillateur
				// (eventuellement stable)
				Plateau tmp = p1.clone();

				// on va calculer pr�cis�ment la p�riode
				periode = cpt2 - cpt1;
				queue = cpt1;

				// la periode est entre 1 et periode et divise (cpt2-cpt1)
				int fin = (int) Math.sqrt(periode) + cpt1;
				for (int j = cpt1 + 1; j <= fin; j++) {
					tmp.etapeSuivante();
					if (periode % (j - cpt1) == 0) {
						if (listesCellulesEgales(structure1, tmp.getMatrice())) {
							periode = j - cpt1;
							continue;
						}
					}

				}

				// on va maintenant calculer la queue
				p1 = p.clone();
				p2 = p.clone();

				for (int j = 0; j < periode; j++) {
					p2.etapeSuivante();
				}

				for (int j = 0; j < queue; j++) {
					if (listesCellulesEgales(p1.getMatrice(), p2.getMatrice())) {
						queue = j;
						continue;
					}
					p1.etapeSuivante();
					p2.etapeSuivante();
				}

				if (periode == 1) {
					return new ResultatAnalyseStable(queue);
				} else {
					return new ResultatAnalyseOscillateur(queue, periode);
				}
			} else if (translation(structure1, p2, translation)) {
				// on sait que l'evolution est de type vaisseau (mais pas
				// oscillateur)
				Plateau tmp = p1.clone();

				// on va calculer precisément la période:

				periode = cpt2 - cpt1;
				queue = cpt1;

				int fin = (int) Math.sqrt(periode) + cpt1;
				for (int j = cpt1 + 1; j <= fin; j++) {
					tmp.etapeSuivante();
					if (periode % (j - cpt1) == 0) {
						if (translation(structure1, tmp, translation)) {
							periode = j - cpt1;
							continue;
						}
					}

				}

				// puis pr�cis�ment la queue
				p1 = p.clone();
				p2 = p.clone();
				for (int j = 0; j < periode; j++) {
					p2.etapeSuivante();
				}

				for (int j = 0; j < queue; j++) {
					if (translation(p1.getMatrice(), p2, translation)) {
						queue = j;
						continue;
					}
					p1.etapeSuivante();
					p2.etapeSuivante();
				}
				return new ResultatAnalyseVaisseau(queue, periode, translation.getX(),
						translation.getY());

			}

			p1.etapeSuivante();
			cpt1++;
			p2.etapeSuivante();
			p2.etapeSuivante();
			cpt2++;
			cpt2++;
		}
		return new ResultatAnalyseIndetermine(d);
	}

	/**
	 * verifie que deux matrice trié contiennent les même éléments
	 * 
	 * @param liste1
	 * @param liste2
	 * @return vrai si elles contiennent les mêmes éléments. faux sinon
	 */
	public static boolean listesCellulesEgales(Matrice liste1, Matrice liste2) {
		if (liste1.length() != liste2.length())// on verifie que les deux listes
												// font la même taille n
			return false;

		Iterator<Cell> itr1 = liste1.iterator();
		Iterator<Cell> itr2 = liste2.iterator();

		while (itr1.hasNext()) { // on parcour les n éléments des deux listes
									// (de même tailles).
			if (!itr1.next().equals(itr2.next())) // si l'element i de la liste
													// 1 n'est pas le même que
													// l'élement i de la liste 2
				return false;
		}
		// (les deux listes sont triés donc si elles contiennent les meme
		// élements ils apparaissent au même endroit)
		return true;
	}

	/**
	 * dit si deux matrices sont identique à une translation pres. (Notons que
	 * nous ne ne considèrons pas uniquement les cellules vivantes mais aussi
	 * les cellules mortes. De ce fait si le plateau considéré est un plateau
	 * fermé alors alors on renvoit faux systematiquement)
	 * 
	 * @param matrice
	 *            première matrice
	 * @param p
	 *            Plateau contenant la deuxième matrice (en fonction du type du
	 *            plateau la translation est differentes)
	 * @param pos
	 *            variable de position qui contiendra le vecteur de la
	 *            translation entre liste1 et le plateau p si la fonction
	 *            renvoit vrai
	 * 
	 * @return vrai si les deux listes de cellules sont identique à une
	 *         translation pres faux sinon
	 */
	public static boolean translation(Matrice matrice, Plateau p, Position pos) {
		assert (pos != null);
		return p.estUneTranslation(matrice, pos);
	}

}
